//
// Created by zy on 22-5-7.
//
#include <cassert>
#include <chrono>
#include <iostream>
#include "leveldb/db.h"
#include "leveldb/comparator.h"
#include "util.h"
#include "stats.h"
#include "learned_index.h"
#include <cstring>
#include "cxxopts.hpp"
#include <unistd.h>
#include <fstream>
#include "../db/version_set.h"
#include <cmath>
#include <random>

using namespace std::chrono;
using namespace leveldb;
using namespace adgMod;
using std::string;
using std::cerr;
using std::cout;
using std::endl;
using std::to_string;
using std::vector;
using std::map;
using std::ifstream;
using std::string;

int num_pairs_base = 1000;
int mix_base = 20;



class NumericalComparator : public Comparator {
 public:
  NumericalComparator() = default;
  virtual const char* Name() const {return "adgMod:NumericalComparator";}
  virtual int Compare(const Slice& a, const Slice& b) const {
    uint64_t ia = adgMod::ExtractInteger(a.data(), a.size());
    uint64_t ib = adgMod::ExtractInteger(b.data(), b.size());
    if (ia < ib) return -1;
    else if (ia == ib) return 0;
    else return 1;
  }
  virtual void FindShortestSeparator(std::string* start, const Slice& limit) const { return; };
  virtual void FindShortSuccessor(std::string* key) const { return; };
};


void PutAndPrefetch(int lower, int higher, vector<string>& keys) {
  adgMod::Stats* instance = adgMod::Stats::GetInstance();

  Status status;

  instance->StartTimer(9);
  for (int i = lower; i < higher; ++i) {
    string value = generate_value(0);
    status = db->Put(write_options, keys[i], value);
    assert(status.ok() && "File Put Error");
  }
  instance->PauseTimer(9, true);

  //cout << "Put Complete" << endl;


  instance->StartTimer(10);
  for (int i = lower; i < higher; ++i) {
    string value;
    status = db->Get(read_options, keys[i], &value);
    //cout << "Get " << keys[i] << " Done" << endl;
    assert(status.ok() && "File Get Error");
  }
  instance->PauseTimer(10, true);

  //cout << "Prefetch Complete" << endl;
};

enum LoadType {
  Ordered = 0,
  Reversed = 1,
  ReversedChunk = 2,
  Random = 3,
  RandomChunk = 4
};

int main(int argc, char *argv[]) {
  int num_operations, num_iteration, num_mix;
  float test_num_segments_base;
  float num_pair_step;
  string db_location, profiler_out, input_filename, distribution_filename, ycsb_filename;
  bool print_single_timing, print_file_info, evict, unlimit_fd, use_distribution = false, pause, use_ycsb = false;
  bool change_level_load, change_file_load, change_level_learning, change_file_learning;
  int load_type, insert_bound, length_range;
  string db_location_copy;

  cxxopts::Options commandline_options("leveldb read test", "Testing leveldb read performance.");
  commandline_options.add_options()
      ("n,get_number", "the number of gets (to be multiplied by 1024)", cxxopts::value<int>(num_operations)->default_value("1000"))
          ("s,step", "the step of the loop of the size of db", cxxopts::value<float>(num_pair_step)->default_value("1"))
              ("i,iteration", "the number of iterations of a same size", cxxopts::value<int>(num_iteration)->default_value("1"))
                  ("m,modification", "if set, run our modified version", cxxopts::value<int>(adgMod::MOD)->default_value("0"))
                      ("h,help", "print help message", cxxopts::value<bool>()->default_value("false"))
                          ("d,directory", "the directory of db", cxxopts::value<string>(db_location)->default_value("/mnt/ssd/testdb"))
                              ("k,key_size", "the size of key", cxxopts::value<int>(adgMod::key_size)->default_value("8"))
                                  ("v,value_size", "the size of value", cxxopts::value<int>(adgMod::value_size)->default_value("8"))
                                      ("single_timing", "print the time of every single get", cxxopts::value<bool>(print_single_timing)->default_value("false"))
                                          ("file_info", "print the file structure info", cxxopts::value<bool>(print_file_info)->default_value("false"))
                                              ("test_num_segments", "test: number of segments per level", cxxopts::value<float>(test_num_segments_base)->default_value("1"))
                                                  ("string_mode", "test: use string or int in model", cxxopts::value<bool>(adgMod::string_mode)->default_value("false"))
                                                      ("e,model_error", "error in modesl", cxxopts::value<uint32_t>(adgMod::model_error)->default_value("8"))
                                                          ("f,input_file", "the filename of input file", cxxopts::value<string>(input_filename)->default_value(""))
                                                              ("multiple", "test: use larger keys", cxxopts::value<uint64_t>(adgMod::key_multiple)->default_value("1"))
                                                                  ("w,write", "writedb", cxxopts::value<bool>(fresh_write)->default_value("false"))
                                                                      ("c,uncache", "evict cache", cxxopts::value<bool>(evict)->default_value("false"))
                                                                          ("u,unlimit_fd", "unlimit fd", cxxopts::value<bool>(unlimit_fd)->default_value("false"))
                                                                              ("x,dummy", "dummy option")
                                                                                  ("l,load_type", "load type", cxxopts::value<int>(load_type)->default_value("0"))
                                                                                      ("filter", "use filter", cxxopts::value<bool>(adgMod::use_filter)->default_value("false"))
                                                                                          ("mix", "mix read and write", cxxopts::value<int>(num_mix)->default_value("0"))
                                                                                              ("distribution", "operation distribution", cxxopts::value<string>(distribution_filename)->default_value(""))
                                                                                                  ("change_level_load", "load level model", cxxopts::value<bool>(change_level_load)->default_value("false"))
                                                                                                      ("change_file_load", "enable level learning", cxxopts::value<bool>(change_file_load)->default_value("false"))
                                                                                                          ("change_level_learning", "load file model", cxxopts::value<bool>(change_level_learning)->default_value("false"))
                                                                                                              ("change_file_learning", "enable file learning", cxxopts::value<bool>(change_file_learning)->default_value("false"))
                                                                                                                  ("p,pause", "pause between operation", cxxopts::value<bool>(pause)->default_value("false"))
                                                                                                                      ("policy", "learn policy", cxxopts::value<int>(adgMod::policy)->default_value("0"))
                                                                                                                          ("YCSB", "use YCSB trace", cxxopts::value<string>(ycsb_filename)->default_value(""))
                                                                                                                              ("insert", "insert new value", cxxopts::value<int>(insert_bound)->default_value("0"))
                                                                                                                                  ("range", "use range query and specify length", cxxopts::value<int>(length_range)->default_value("0"));
  auto result = commandline_options.parse(argc, argv);
  if (result.count("help")) {
    printf("%s", commandline_options.help().c_str());
    exit(0);
  }

  std::default_random_engine e1(0), e2(255), e3(0);
  srand(0);
  num_operations *= num_pairs_base;  //1000 * 1000,什么意思呢？
  db_location_copy = db_location;

  adgMod::fd_limit = unlimit_fd ? 1024 * 1024 : 1024;  //TODO: 需要测试一下是否真的影响性能
  adgMod::restart_read = true;
  adgMod::level_learning_enabled ^= change_level_learning;
  adgMod::file_learning_enabled ^= change_file_learning;
  adgMod::load_level_model ^= change_level_load;
  adgMod::load_file_model ^= change_file_load;

  // adgMod::file_learning_enabled = false;


  vector<string> keys;
  vector<uint64_t> distribution;  //uint64_t是unsigned long int的typedef别名
  vector<int> ycsb_is_write;
  //keys.reserve(100000000000 / adgMod::value_size);

  //开始读取数据
  std::cerr << "读取keys..." << "\r";
  time_point<system_clock> start_1st_readkey = system_clock::now();
  if (!input_filename.empty()) {
    ifstream input(input_filename);
    string key;
    while (input >> key) {
      string the_key = generate_key(key);
      keys.push_back(std::move(the_key));
    }
    //adgMod::key_size = (int) keys.front().size();
  } else {
    std::uniform_int_distribution<uint64_t> udist_key(0, 999999999999999);
    for (int i = 0; i < 10000000; ++i) {
      keys.push_back(generate_key(to_string(udist_key(e2))));
    }
  }
  time_point<system_clock> end_1st_readkey  = system_clock::now();
  std::chrono::duration<double> elapsed_1st_readkey  = end_1st_readkey  - start_1st_readkey ;
  //    std::cerr << "读取keys完成，Elapsed time: " << elapsed_1st_readkey.count()/60 << "min" << endl;

  if (!distribution_filename.empty()) {
    use_distribution = true;
    ifstream input(distribution_filename);
    uint64_t index;
    while (input >> index) {
      distribution.push_back(index);
    }
  }

  if (!ycsb_filename.empty()) {
    use_ycsb = true;
    use_distribution = true;
    ifstream input(ycsb_filename);
    uint64_t index;
    int is_write;
    while (input >> is_write >> index) {
      distribution.push_back(index);
      ycsb_is_write.push_back(is_write);
    }
  }
  bool copy_out = num_mix != 0 || use_ycsb;

  // 在load_ar的时候copy_out是false
  adgMod::Stats* instance = adgMod::Stats::GetInstance();
  vector<vector<size_t>> times(20);
  string values(1024 * 1024, '0');

  if (copy_out) {
    system("sync; echo 3 | sudo tee /proc/sys/vm/drop_caches");
  }

  if (num_mix > 1000) {
    mix_base = 1000;
    num_mix -= 1000;
  }

  for (size_t iteration = 0; iteration < num_iteration; ++iteration) {
    if (copy_out) {
      system("sudo fstrim -a -v");
    }

    //        std::cerr << "第 " << iteration << " /共 " << num_iteration << " 个iteration" << endl;

    db_location = db_location_copy;  //todo: 这个怎么复制过来复制过去的？
    std::uniform_int_distribution<uint64_t > uniform_dist_file(0, (uint64_t) keys.size() - 1);
    std::uniform_int_distribution<uint64_t > uniform_dist_file2(0, (uint64_t) keys.size() - 1);
    std::uniform_int_distribution<uint64_t > uniform_dist_value(0, (uint64_t) values.size() - adgMod::value_size - 1);
    // std::uniform_int_distribution模板定义了分布对象，它返回的是均匀分布在闭合范围[a, b]内的随机整数。模板的参数类型是生成的整数类型，默认是int：
    //定义在类中的类型别名result_type和分布所生成的值的类型相对应。模板参数只接受整数类型。


    DB* db;
    Options options;
    ReadOptions& read_options = adgMod::read_options;
    WriteOptions& write_options = adgMod::write_options;
    Status status;

    options.create_if_missing = true;
    //options.comparator = new NumericalComparator;
    //adgMod::block_restart_interval = options.block_restart_interval = adgMod::MOD == 8 || adgMod::MOD == 7 ? 1 : adgMod::block_restart_interval;
    //read_options.fill_cache = true;
    write_options.sync = false;
    instance->ResetAll();


    if (fresh_write && iteration == 0) {
      string command = "rm -rf " + db_location;
      system(command.c_str());  //应该是STL的string不能command执行，故换成自带的string格式

      std::cerr << "正在执行sudo fstrim -a -v，大概用时两分钟" << endl;
      time_point<system_clock> start_fstrim = system_clock::now();
      system("sudo fstrim -a -v");  //这句话要sudo权限， 否则不执行， 最好跑的时候就sudo，或保证当前用户可以不输密码跑sudo
      time_point<system_clock> end_fstrim = system_clock::now();
      std::chrono::duration<double> elapsed_fstrim = end_fstrim - start_fstrim;
      std::cerr << "fstrim完成，用时: " << elapsed_fstrim.count() << "s" << endl;

      system("sync; echo 3 | sudo tee /proc/sys/vm/drop_caches");
      cout << "delete and trim complete" << endl;

      //            std::cerr << "line 256: 第一次db->Open(): 开始" << endl;
      auto start_open1 = system_clock::now();
      status = DB::Open(options, db_location, &db);
      time_point<system_clock> end_open1 = system_clock::now();
      std::chrono::duration<double> elapsed_open1 = end_open1 - start_open1;
      //            std::cerr << "line 270: 第一次db->Open(): 结束，用时: " << elapsed_open1.count() << "s" <<  endl;

      // Status={leveldb::Status} < state_={const char*}NULL >
      assert(status.ok() && "Open Error");


      instance->StartTimer(9);
      int cut_size;    // 小于10万和大于10万分开讨论
      if (keys.size() < 100000){
        cut_size = keys.size() / 1000;
        //                std::cerr << "every 10k as a chunk; " << keys.size() << "keys in total, devided to " << cut_size << "chunks" << endl;
      } else{
        cut_size = keys.size() / 100000;
        //                std::cerr << "every 0.1M as a chunk; " << keys.size() << "keys in total, devided to " << cut_size << "chunks" << endl;
      }
      std::vector<std::pair<int, int>> chunks;
      switch (load_type) {
        case Ordered: {
          for (int cut = 0; cut < cut_size; ++cut) {
            chunks.emplace_back(keys.size() * cut / cut_size, keys.size() * (cut + 1) / cut_size);
          }  // chunks每一份的大小为keys.size()/cut_size，在无法整除时，每一份大小会比10w略多
          break;
        }
        case ReversedChunk: {
          for (int cut = cut_size - 1; cut >= 0; --cut) {
            chunks.emplace_back(keys.size() * cut / cut_size, keys.size() * (cut + 1) / cut_size);
          }
          break;
        }
        case Random: {
          std::random_shuffle(keys.begin(), keys.end());
          for (int cut = 0; cut < cut_size; ++cut) {
            chunks.emplace_back(keys.size() * cut / cut_size, keys.size() * (cut + 1) / cut_size);
          }
          break;
        }
        case RandomChunk: {
          for (int cut = 0; cut < cut_size; ++cut) {
            chunks.emplace_back(keys.size() * cut / cut_size, keys.size() * (cut + 1) / cut_size);
          }
          std::random_shuffle(chunks.begin(), chunks.end());
          break;
        }
        default: assert(false && "Unsupported load type.");
      }

      auto start_chunk = system_clock::now();
      std::cerr << endl << "line308: 开始chuck写入, 会在ssd文件夹生成ldb文件, 大概用时12-15分钟" << endl;
      for (int cut = 0; cut < chunks.size(); ++cut) {  //todo: 注意这里的++cut是循环进行一轮之后执行的，不是进入循环之后执行
        std::cerr << "leveldb writing process: " << cut << " / " << chunks.size() << "\r";
        for (int i = chunks[cut].first; i < chunks[cut].second; ++i) {
          status = db->Put(write_options, keys[i], {values.data() + uniform_dist_value(e2), (uint64_t) adgMod::value_size});
          // values.data()是const char* 指针，指向1024*1024个0,如果用于初始化string则得到相应的1024*1024长度全0的string。
          // uniform_dist_value(e2)是unsigned long类型，无法直接用于初始化string。
          //                    auto a12 = values.data() + uniform_dist_value(e2);
          //                    string a123 = values.data() + uniform_dist_value(e2);
          //                    auto a3 = (uint64_t) adgMod::value_size;
          //                    string putted_delete = {values.data() + uniform_dist_value(e2), (uint64_t) adgMod::value_size};
          // putted_delete.size(): unsigned long 64
          //todo: 上半句中write_options有sync=false，这个意思是？
          //values.size() 1048576, values.data() const char*;
          assert(status.ok() && "File Put Error");  //todo: 每一个chunk检查下是否有error？意义何在，为什么一个chunk检查一次？
        }
      }
      time_point<system_clock> end_chunk = system_clock::now();
      std::chrono::duration<double> elapsed_chunk = end_chunk - start_chunk;
      //            std::cerr << endl << "line 300: chunk写入完成，用时: " << elapsed_chunk.count() << "s" <<  endl;

      //            std::cerr << "line 338: >>>>>>>>>>>>>>>数据库初始化后的一些操作: 开始<<<<<<<<<<<<<<<<<" << endl;
      //            std::cerr << "line 339:  db->vlog->Sync()......" << "\r";
      auto start_sync = system_clock::now();
      adgMod::db->vlog->Sync();
      time_point<system_clock> end_sync = system_clock::now();
      std::chrono::duration<double> elapsed_sync = end_sync - start_sync;
      //            std::cerr << "line 343:  db->vlog->Sync()：用时: " << elapsed_sync.count() << "s" <<  endl;

      instance->PauseTimer(9, true);  //这个是什么意思呢，暂停什么，为什么暂停？
      cout << "Put Complete" << endl;

      //            std::cerr << "line 340:  keys.clear()，大概用时6-3分钟..." << endl;
      auto start_clr_keys = system_clock::now();
      keys.clear();
      time_point<system_clock> end_clr_keys = system_clock::now();
      std::chrono::duration<double> elapsed_clr_keys = end_clr_keys - start_clr_keys;
      //            std::cerr << "line 345:  keys.clear()：用时: " << elapsed_clr_keys.count() << "s" <<  endl;

      //            std::cerr << "line 357:  db->PrintFileInfo()......" << "\r";
      auto start_prt_fileinfo = system_clock::now();
      if (print_file_info && iteration == 0) db->PrintFileInfo();
      time_point<system_clock> end_prt_fileinfo = system_clock::now();
      std::chrono::duration<double> elapsed_prt_fileinfo = end_prt_fileinfo - start_prt_fileinfo;
      //            std::cerr << "line 360:  db->PrintFileInfo()：用时: " << elapsed_prt_fileinfo.count() << "s" <<  endl;

      //            std::cerr << "line 347:  WaitForBackground......" << "\r";
      auto start_wait_bg = system_clock::now();
      adgMod::db->WaitForBackground();
      time_point<system_clock> end_wait_bg = system_clock::now();
      std::chrono::duration<double> elapsed_ld = end_wait_bg - start_wait_bg;
      //            std::cerr << "line 352:  WaitForBackground：用时: " << elapsed_ld.count() << "s" <<  endl;

      std::cerr << endl << "line 362:  delete db......" << endl;
      auto start_deldb1_ini = system_clock::now();
      delete db;  // 第一次deletedb，目的是为了持久化memtable文件
      time_point<system_clock> end_deldb1_ini = system_clock::now();
      std::chrono::duration<double> elapsed_deldb1_ini = end_deldb1_ini - start_deldb1_ini;
      //            std::cerr << "line 366:  delete db：用时: " << elapsed_deldb1_ini.count() << "s" <<  endl;
      //            std::cerr << "line 367: >>>>>>>>>>>>>>>数据库初始化后的一些操作: 结束<<<<<<<<<<<<<<<<<" << endl;

      //            std::cerr << "line 355: 第二次db->Open(): 开始" << "\r";
      auto start_open2 = system_clock::now();
      status = DB::Open(options, db_location, &db);
      time_point<system_clock> end_open2 = system_clock::now();
      std::chrono::duration<double> elapsed_open2 = end_open2 - start_open2;
      //            std::cerr << "line 360: 第二次db->Open(): 结束，用时: " << elapsed_open2.count() << "s" <<  endl;

      //            std::cerr << "line 362: 第二次Open()后的WaitForBackground: 开始" << "\r";
      auto start_bg2 = system_clock::now();
      adgMod::db->WaitForBackground();
      time_point<system_clock> end_bg2 = system_clock::now();
      std::chrono::duration<double> elapsed_bg2 = end_bg2 - start_bg2;
      //            std::cerr << "line 367: 第二次Open()后的WaitForBackground：结束，用时: " << elapsed_bg2.count() << "s" <<  endl;

      if (adgMod::MOD == 6 || adgMod::MOD == 7) { //如果设定了，跑我们修改后的版本
        Version* current = adgMod::db->versions_->current();

        for (int i = 1; i < config::kNumLevels; ++i) {  // 默认是7个 level
                                                        //                    std::cerr << "line 337: 开始level learn at level: " << i << "\r";
          auto start_lv = system_clock::now();

          LearnedIndexData::Learn(new VersionAndSelf{current, adgMod::db->version_count, current->learned_index_data_[i].get(), i});

          time_point<system_clock> end_lv = system_clock::now();
          std::chrono::duration<double> elapsed_lv = end_lv - start_lv;
          //                    std::cerr << "line 394,learn level " << i << "用时: " << elapsed_lv.count() << "s" <<  endl;
        }

        //                std::cerr << "line 388: 训练: current->FileLearn(): 开始" << "\r";
        auto start_file_learn = system_clock::now();
        current->FileLearn();
        time_point<system_clock> end_file_learn = system_clock::now();
        std::chrono::duration<double> elapsed_file_learn = end_file_learn - start_file_learn;
        //                std::cerr << "line 383: 训练: current->FileLearn()：结束，用时: " << elapsed_file_learn.count() << "s" <<  endl;
      }

      //            std::cout << "Shutting down" << endl;
      // Put complete he shutting down finished in a short time.
      //            std::cerr << "line 407: 训练后的WaitForBackground: 开始" << endl;
      auto start_waitbg_train = system_clock::now();
      adgMod::db->WaitForBackground();
      time_point<system_clock> end_waitbg_train = system_clock::now();
      std::chrono::duration<double> elapsed_waitbg_train = end_waitbg_train - start_waitbg_train;
      //            std::cerr << "line 412: 训练后的WaitForBackground：结束，用时: " << elapsed_waitbg_train.count() << "s" <<  endl;

      //            std::cerr << "line 414: 训练后delete db: 开始" << endl;
      auto start_deldb_train = system_clock::now();
      delete db;  //delete db的时候model和fmodel的模型才会被保存。
      time_point<system_clock> end_deldb_train = system_clock::now();
      std::chrono::duration<double> elapsed_deldb_train = end_deldb_train - start_deldb_train;
      //            std::cerr << "line 419: 训练后delete db：结束，用时: " << elapsed_deldb_train.count() << "s" <<  endl;
      // 这里后续产生了.fmodel文件

      //keys.reserve(100000000000 / adgMod::value_size);
      //如果前面的加载是random类型的，那么keys就是被打乱的，所以前面删除了keys，这里重新读取。
      //            std::cerr << "line424: 重新加载keys" << endl;
      auto start_reload_keys = system_clock::now();
      if (!input_filename.empty()) {
        ifstream input(input_filename);
        string key;
        while (input >> key) {
          string the_key = generate_key(key);
          keys.push_back(std::move(the_key));
        }
        adgMod::key_size = (int) keys.front().size();
      }
      time_point<system_clock> end_reload_keys = system_clock::now();
      std::chrono::duration<double> elapsed_reload_keys = end_reload_keys - start_reload_keys;
      //            std::cerr << "line 437: 训练后delete db：结束，用时: " << elapsed_reload_keys.count() << "s" <<  endl;
      //            std::cerr << "重新加载keys： 完成" << endl;

      fresh_write = false;
    }


    if (copy_out) {
      string db_location_mix = db_location + "_mix";
      string remove_command = "rm -rf " + db_location_mix;
      string copy_command = "cp -r " + db_location + " " + db_location_mix;

      system(remove_command.c_str());
      system(copy_command.c_str());
      db_location = db_location_mix;
    }

    // evict policy: 缓存淘汰策略
    if (evict) system("sync; echo 3 | sudo tee /proc/sys/vm/drop_caches");  //evict:

    cout << "Starting up" << endl;  // time after starting up is very long.
    std::cerr << "line 459: 第三次db->Open(): 开始" << endl;
    auto start_open3 = system_clock::now();
    status = DB::Open(options, db_location, &db);
    time_point<system_clock> end_open3 = system_clock::now();
    std::chrono::duration<double> elapsed_open3 = end_open3 - start_open3;
    std::cerr << "line 464: 第三次db->Open(): 结束，用时: " << elapsed_open3.count() << "s" <<  endl;

    std::cerr << "line 362: 第三次Open()后的WaitForBackground: 开始" << endl;
    auto start_bg3 = system_clock::now();
    adgMod::db->WaitForBackground();
    time_point<system_clock> end_bg3 = system_clock::now();
    std::chrono::duration<double> elapsed_bg3 = end_bg3 - start_bg3;
    //        std::cerr << "line 367: 第三次Open()后的WaitForBackground：结束，用时: " << elapsed_bg3.count() << "s" <<  endl;

    assert(status.ok() && "Open Error");

    uint64_t last_read = 0, last_write = 0;
    int last_level = 0, last_file = 0, last_baseline = 0, last_succeeded = 0, last_false = 0, last_compaction = 0, last_learn = 0;
    std::vector<uint64_t> detailed_times;
    bool start_new_event = true;

    instance->StartTimer(13);
    uint64_t write_i = 0;
    //        std::cerr << "line 483 reached, 开始测试" << endl;
    //        if ((num_operations > distribution.size()) && (distribution.size() != 0) ) {
    //            num_operations = distribution.size();
    //        }
    for (int i = 0; i < num_operations; ++i) {
      //            std::cerr << "current i: " << i << endl;
      if (i % 10000 == 0){
        std::cerr << "iteration: " << iteration << ", operation percent: " << i << "/" << num_operations << "\r";
      }
      //            std::cerr << "operation percent: " << i/100 << endl;

      if (start_new_event) {
        detailed_times.push_back(instance->GetTime());
        start_new_event = false;
      }

      // todo: 这个write是怎么判断的
      // todo: length_range 又是为什么这么判断
      // 这里的代码实现上，如果ycsb_is_write = 1或2，表示要写（insert或update把应该是）
      // 如果ycsb_is_write不是1或2，表示它表示范围查询，值为范围起始值+100
      // 后面的length_range应该是表示不使用ycsb的时候，length_range自己指定
      bool write = use_ycsb ? ycsb_is_write[i] == 1 || ycsb_is_write[i] == 2 : (i % mix_base) < num_mix;
      length_range = use_ycsb && ycsb_is_write[i] > 2 ? ycsb_is_write[i] - 100 : length_range;
      Iterator* db_iter = length_range == 0 ? nullptr : db->NewIterator(read_options);


      if (write) {
        if (input_filename.empty()) {  // wirte && input_filename为空
                                       //                    throw "shouldn't go this way";
          instance->StartTimer(10);
          status = db->Put(write_options, generate_key(to_string(distribution[i])), {values.data() + uniform_dist_value(e3), (uint64_t) adgMod::value_size});
          instance->PauseTimer(10);
        } else {  // write && input_filename不为空
          uint64_t index;
          if (use_distribution) {  // write && input_filename不为空 && -YCSB不为空
            index = distribution[i];
          } else if (load_type == 0) {  // write && input_filename不为空 && -YCSB为空
            index = write_i++ % keys.size();
          } else {
            index = uniform_dist_file(e1) % (keys.size() - 1);
          }

          instance->StartTimer(10);
          if (use_ycsb && ycsb_is_write[i] == 2) {  // write && input_filename不为空 && -YCSB不为空，对应插入INSERT部分：
            // 送入的文本文件是key本身，送入数据库中的是10G + key
            status = db->Put(write_options, generate_key(to_string(10000000000 + index)), {values.data() + uniform_dist_value(e3), (uint64_t) adgMod::value_size});
          } else { // write && input_filename不为空 && ycsb为空或ycsb对应更新UPDATE部分：
            // 送入的文本文件是一个index，送入数据库中的是从input_filename文件中找到该index对应key
            status = db->Put(write_options, keys[index], {values.data() + uniform_dist_value(e3), (uint64_t) adgMod::value_size});
          }
          instance->PauseTimer(10);
          assert(status.ok() && "Mix Put Error");
          //cout << index << endl;
        }
      } else if (length_range != 0) {  // wirte=false, length_range!=0, 对应SCAN。
                                       //                throw "shouldn't go this way";
        // Seek
        if (input_filename.empty()) {
          instance->StartTimer(4);
          const string& key = generate_key(to_string(distribution[i]));
          db_iter->Seek(key);
          instance->PauseTimer(4);
        } else {
          uint64_t index = use_distribution ? distribution[i] : uniform_dist_file2(e2) % (keys.size() - 1);
          if (insert_bound != 0 && index > insert_bound) {  // (length_range: 81)181 (index)6497333951169773611 形状的
            const string& key = generate_key(to_string(10000000000 + index));  // todo: 有个隐患是，index+10G后可能会爆掉19位数，导致内存被乱改？或者代码逻辑出错？

            instance->StartTimer(4);
            db_iter->Seek(key);
            instance->PauseTimer(4);
          } else {  // (length_range: 09)109 (index)7110 形状的
            index = index >= length_range ? index - length_range : 0;
            const string& key = keys[index];

            instance->StartTimer(4);
            db_iter->Seek(key);
            instance->PauseTimer(4);
          }


        }

        // Range
        instance->StartTimer(17);
        for (int r = 0; r < length_range; ++r) {
          if (!db_iter->Valid()) break;
          Slice key = db_iter->key();
          string value = db_iter->value().ToString();
          // cout << key.ToString() << value << endl;
          // value.clear();
          db_iter->Next();
        }
        instance->PauseTimer(17);
      } else {  // 这里对应非write，并且length range == 0
                //                throw "shouldn't go this way";
        string value;
        if (input_filename.empty()) {
          instance->StartTimer(4);
          status = db->Get(read_options, generate_key(to_string(distribution[i])), &value);
          instance->PauseTimer(4);
          if (!status.ok()) {
            cout << distribution[i] << " Not Found" << endl;
            //assert(status.ok() && "File Get Error");
          }
        } else { // 这里对应非write，并且length range == 0; 并且input_filename非空。但仍旧，570行的insert_bound无缘由。
          uint64_t index = use_distribution ? distribution[i] : uniform_dist_file2(e2) % (keys.size() - 1);
          // deprecated const string& key;  // 这一行key的赋值应该移到下文中的else里面，index可能是key本身，因此在keys中不一定索引地到
          instance->StartTimer(4);
          if (insert_bound != 0 && index > insert_bound) {  //TODO：这里读超出范围的条目是自动生成来补的，不太好
            const string& key = generate_key(to_string(10000000000 + index));
            status = db->Get(read_options, key, &value);
            if (!status.ok()) {
              cout << key << " Not Found" << endl;
            }
          } else {
            const string& key = keys[index];
            status = db->Get(read_options, key, &value);
            if (!status.ok()) {
              cout << key << " Not Found" << endl;
            }
          }

          instance->PauseTimer(4);
        }
      }

      if (pause) {
        if ((i + 1) % (num_operations / 10000) == 0) ::usleep(800000);
      }

      if ((i + 1) % (num_operations / 100) == 0) detailed_times.push_back(instance->GetTime());
      if ((i + 1) % (num_operations / 10) == 0) {
        int level_read = levelled_counters[0].Sum();
        int file_read = levelled_counters[1].Sum();
        int baseline_read = levelled_counters[2].Sum();
        int succeeded_read = levelled_counters[3].NumSum();
        int false_read = levelled_counters[4].NumSum();

        compaction_counter_mutex.Lock();
        int num_compaction = events[0].size();
        compaction_counter_mutex.Unlock();
        learn_counter_mutex.Lock();
        int num_learn = events[1].size();
        learn_counter_mutex.Unlock();

        uint64_t read_time = instance->ReportTime(4);
        uint64_t write_time = instance->ReportTime(10);
        std::pair<uint64_t, uint64_t> time = {detailed_times.front(), detailed_times.back()};

        events[2].push_back(new WorkloadEvent(time, level_read - last_level, file_read - last_file, baseline_read - last_baseline,
                                              succeeded_read - last_succeeded, false_read - last_false, num_compaction - last_compaction, num_learn - last_learn,
                                              read_time - last_read, write_time - last_write, std::move(detailed_times)));

        last_level = level_read;
        last_file = file_read;
        last_baseline = baseline_read;
        last_succeeded = succeeded_read;
        last_false = false_read;
        last_compaction = num_compaction;
        last_learn = num_learn;
        last_read = read_time;
        last_write = write_time;
        detailed_times.clear();
        start_new_event = true;
        cout << (i + 1) / (num_operations / 10) << endl;
        Version* current = adgMod::db->versions_->current();
        printf("LevelSize %d %d %d %d %d %d\n", current->NumFiles(0), current->NumFiles(1), current->NumFiles(2), current->NumFiles(3),
               current->NumFiles(4), current->NumFiles(5));
      }
      delete db_iter;


    }
    //        std::cerr << "\n测试完成 at line 628" << endl;

    instance->PauseTimer(13, true);



    instance->ReportTime();
    for (int s = 0; s < times.size(); ++s) {
      times[s].push_back(instance->ReportTime(s));
    }
    adgMod::db->WaitForBackground();
    sleep(10);



    for (auto& event_array : events) {
      for (Event* e : event_array) e->Report();
    }

    for (Counter& c : levelled_counters) c.Report();

    file_data->Report();

    for (auto it : file_stats) {
      printf("FileStats %d %d %lu %lu %u %u %lu %d\n", it.first, it.second.level, it.second.start,
             it.second.end, it.second.num_lookup_pos, it.second.num_lookup_neg, it.second.size, it.first < file_data->watermark ? 0 : 1);
    }

    adgMod::learn_cb_model->Report();

    //        std::cerr << "line 658: 最后测试完成后--delete db_iter: 开始" << endl;
    auto start_deldbiter_end = system_clock::now();
    time_point<system_clock> end_deldbiter_end = system_clock::now();
    std::chrono::duration<double> elapsed_deldbiter_end = end_deldbiter_end - start_deldbiter_end;
    //        std::cerr << "line 663: 最后测试完成后--delete db_iter：结束，用时: " << elapsed_deldbiter_end.count() << "s" <<  endl;

    //        std::cerr << "line 665: 最后测试完成后--delete db: 开始" << "\r";
    auto start_deldb_end = system_clock::now();
    // db是一个{leveldb::DBImpl *}指针，在其执行析构函数的时候，也即leveldb::DBImpl::~DBImpl时调用delete versions_
    // versions_是一个
    delete db;
    time_point<system_clock> end_deldb_end = system_clock::now();
    std::chrono::duration<double> elapsed_deldb_end = end_deldb_end - start_deldb_end;
    //        std::cerr << "line 670: 最后测试完成后--delete db：结束，用时: " << elapsed_deldb_end.count() << "s" <<  endl;
  }  // 整个iteration结束

  for (int s = 0; s < times.size(); ++s) {
    vector<uint64_t>& time = times[s];
    vector<double> diff(time.size());
    if (time.empty()) continue;

    double sum = std::accumulate(time.begin(), time.end(), 0.0);
    double mean = sum / time.size();
    std::transform(time.begin(), time.end(), diff.begin(), [mean] (double x) { return x - mean; });
    double stdev = std::sqrt(std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0) / time.size());

    printf("Timer %d MEAN: %lu, STDDEV: %f\n", s, (uint64_t) mean, stdev);
  }

  if (num_iteration > 1) {
    cout << "Data Without the First Item" << endl;
    for (int s = 0; s < times.size(); ++s) {
      vector<uint64_t>& time = times[s];
      vector<double> diff(time.size() - 1);
      if (time.empty()) continue;

      double sum = std::accumulate(time.begin() + 1, time.end(), 0.0);
      double mean = sum / (time.size() - 1);
      std::transform(time.begin() + 1, time.end(), diff.begin(), [mean] (double x) { return x - mean; });
      double stdev = std::sqrt(std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0) / time.size());

      printf("Timer %d MEAN: %lu, STDDEV: %f\n", s, (uint64_t) mean, stdev);
    }
  }
}


// load_db.sh
// 参数为 -f ../db/dataset/osm_ny.txt -k 16 -v 64 -d ../ssd/db_osm -m 7 -w > ../evaluation/osm_put.txt
